from dataclasses import dataclass, field
from typing import Set, List, Tuple, Union

import wx

from event_bus import EventBus


@dataclass(eq=False, order=False)
class SampleUnit:
    item: wx.TreeItemId = None
    parent: wx.TreeItemId = None
    fingerprint: str = ""
    crop_start: Tuple[int, int] = (0, 0)
    crop_size: Tuple[int, int] = (0, 0)
    categories: Set[str] = field(default_factory=set)

    def __eq__(self, other):
        return (hasattr(other, "fingerprint") and self.fingerprint == other.fingerprint) or \
               (isinstance(other, str) and self.fingerprint == other) or \
               (isinstance(other, wx.TreeItemId) and self.item == other)

    def __hash__(self):
        return int(self.fingerprint, 16)


@dataclass(eq=False, order=False)
class SourceUnit:
    item: wx.TreeItemId = None
    filename: str = ""
    filepath: str = ""
    fingerprint: str = ""
    samples: Set[SampleUnit] = field(default_factory=set)

    def __eq__(self, other):
        return (hasattr(other, "fingerprint") and self.fingerprint == other.fingerprint) or \
               (isinstance(other, str) and self.fingerprint == other) or \
               (isinstance(other, wx.TreeItemId) and self.item == other)

    def append(self, unit: SampleUnit):
        self.samples.add(unit)

    def remove(self, identity: Union[str, wx.TreeItemId]):
        elem = next(filter(lambda unit: (isinstance(identity, wx.TreeItemId) and unit.item == identity) or
                                        (isinstance(identity, str) and unit == identity),
                           self.samples))
        self.samples.discard(elem)


class SourceCollect:
    _collection: List[SourceUnit] = []

    def __getitem__(self, item):
        for p in self._collection:
            if p == item:
                return p
            for c in p.samples:
                if c == item:
                    return c
        else:
            return None

    def walk(self):
        for unit in self._collection:
            unit.item = yield unit, None
            for child in unit.samples:
                child.parent = unit.item
                child.item = yield child, unit.item

    def clear(self):
        self._collection.clear()

    def append(self, unit: SourceUnit):
        self._collection.append(unit)


class CategoryCollect:
    _categories: Set[str]

    def __init__(self):
        self._categories = set()

    def __iter__(self):
        yield from sorted(self._categories)

    def __len__(self):
        return len(self._categories)

    def clear(self):
        self._categories.clear()

    def append(self, *args):
        self._categories.update(args)
        EventBus().post_topic(event=list(self._categories), topic="category-updated")

    def remove(self, *args):
        for item in args:
            if item in self._categories:
                self._categories.remove(item)
